﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using OpenLS.Chart;
using OpenLS.Drawing;
using OpenLS.Spreadsheet.Chart;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Extensibility;

namespace OpenLS.Spreadsheet.UI
{
    partial class WorksheetCharting
    {
        private class Slicer
        {
            private readonly Range _range;

            public Slicer(Worksheet worksheet)
                : this(worksheet.Selection)
            {

            }
            public Slicer(Range range)
            {
                Contract.Requires(range != null);
                _range = range;
                
                Build();

            }

            private Range Range
            {
                get { return _range; }
            }

            private IEnumerable<Range> GetSlicedRanges()
            {
                switch (Orientation)
                {
                    case SlicerOrientation.Vertical:
                        return this.Range.GetColumnRanges(RangeType.Dynamic);
                    default:
                        return Range.GetRowRanges(RangeType.Dynamic);
                }
                if (Range.RowSpan > Range.ColumnSpan)
                    return Range.GetColumnRanges(RangeType.Dynamic);
                return Range.GetRowRanges(RangeType.Dynamic);
            }
            SlicerOrientation Orientation
            {
                get
                {
                    if (Range.RowSpan > Range.ColumnSpan)
                        return SlicerOrientation.Vertical;
                    return SlicerOrientation.Horizontal;

                }
            }
            enum SlicerOrientation
            {
                Vertical,
                Horizontal
            }
            public IEnumerable<RangeNumericReference> GetValueReferences()
            {
                return this._numericReferences;
            }
            List<RangeNumericReference> _numericReferences;
            void Build()
            {
                _numericReferences = new List<RangeNumericReference>();
                bool first = true;
                var slicedRanges = new List<Range>(GetSlicedRanges());
                if (slicedRanges.Count == 0)
                    return;
                Func<Cell, bool> isNonNumeric = c => c.Value != null & !(c.Value is double);
                bool usesCategory = false;
                if (slicedRanges.Count > 1)
                {
                  //  IEnumerable<Cell> nonNumericCells = Enumerable.Where(slicedRanges[0].GetNonEmptyCells(), isNonNumeric);
                    usesCategory = slicedRanges.Count > 1 &&
                                        Enumerable.Any(Enumerable.Where(slicedRanges[0].GetNonEmptyCells(), c => c != slicedRanges[0][0,0]), isNonNumeric);
                }
                IEnumerable<Range> valueRanges;
                if (usesCategory)
                {
                    valueRanges = Enumerable.Skip(slicedRanges, 1);
                }
                else
                {
                    valueRanges = slicedRanges;
                }
                var firstValues = Enumerable.Select(valueRanges, r => r[0, 0]);
                var usesLegend = Enumerable.Any(firstValues, isNonNumeric);
                var orientation = this.Orientation;
                if (usesLegend)
                {
                    if (usesCategory)
                        this.CategoryReference = new RangeStringReference(GetRangeWithoutLegend(slicedRanges[0], orientation));
                    _numericReferences.AddRange(Enumerable.Select(valueRanges, r => new 
                        RangeNumericReference(GetRangeWithoutLegend(r, orientation))));
                    this._texts = new List<SeriesText>();
                    int index = 0;
                    foreach (var range in valueRanges)
                    {
                        var legendEntry = new LegendEntry {Index = index};
                        _legendEntries.Add(legendEntry);
                        _texts.Add(new SeriesText {Reference = new RangeStringReference(new SingleCellRange(range[0, 0])) });
                        index++;
                    }

                }
                else
                {
                    if (usesCategory)
                        this.CategoryReference = new RangeStringReference(slicedRanges[0]);
                    _numericReferences.AddRange(Enumerable.Select(valueRanges, r => new RangeNumericReference(r)));
                    int index = 0;
                    foreach (var range in valueRanges)
                    {
                        this._legendEntries.Add(new LegendEntry { Index = index });
                        index++;
                    }
                        
                }

            }
            static Range GetRangeWithoutLegend (Range range, SlicerOrientation orientation)
            {
                if (range is MultiCellsRange)
                {
                    CellRelativeAddress address;
                    switch (orientation)
                    {
                            case SlicerOrientation.Vertical:
                            address = new CellRelativeAddress(1, 0);
                            break;
                        default:
                            address = new CellRelativeAddress(0, 1);

                            break;
                    }
                    return CellsRange.Create(range.Worksheet,
                                             new RangeAddress(range.Address.TopLeft + address, range.Address.BottomRight));
                }
                throw new NotImplementedException();
            }
            

            DataSource CategoryReference;
            List<LegendEntry> _legendEntries = new List<LegendEntry>();
            public IEnumerable<LegendEntry> GetLegendEntries()
            {
                int index = 0;
                foreach (var range in this.GetSlicedRanges())
                {
                    yield return new LegendEntry { Index = index };
                    index++;
                }
            }
            public int ReferenceCount { get { return Enumerable.Count(this.GetSlicedRanges()); } }
            public IEnumerable<T> GetValueSeries<T>(Func<RangeNumericReference, DataSource, T> factory) where T : Series, new()
            {
                int index = 0;
                foreach (var reference in GetValueReferences())
                {
                    var series = factory(reference, this.CategoryReference);
                    if (_texts != null)
                        series.Text = _texts[index];
                    series.Index = index;
                    series.Order = index;
                    yield return series;
                    index++;
                }
            }
            private List<SeriesText> _texts;
        }
    }
}